// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/metrics/histogram_snapshot_manager.h"

#include <string>
#include <vector>

#include "base/macros.h"
#include "base/metrics/histogram_delta_serialization.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

class HistogramFlattenerDeltaRecorder : public HistogramFlattener {
public:
    HistogramFlattenerDeltaRecorder() { }

    void RecordDelta(const HistogramBase& histogram,
        const HistogramSamples& snapshot) override
    {
        recorded_delta_histogram_names_.push_back(histogram.histogram_name());
        ASSERT_FALSE(ContainsKey(recorded_delta_histogram_sum_,
            histogram.histogram_name()));
        // Keep pointer to snapshot for testing. This really isn't ideal but the
        // snapshot-manager keeps the snapshot alive until it's "forgotten".
        recorded_delta_histogram_sum_[histogram.histogram_name()] = snapshot.sum();
    }

    void InconsistencyDetected(HistogramBase::Inconsistency problem) override
    {
        ASSERT_TRUE(false);
    }

    void UniqueInconsistencyDetected(
        HistogramBase::Inconsistency problem) override
    {
        ASSERT_TRUE(false);
    }

    void InconsistencyDetectedInLoggedCount(int amount) override
    {
        ASSERT_TRUE(false);
    }

    void Reset()
    {
        recorded_delta_histogram_names_.clear();
        recorded_delta_histogram_sum_.clear();
    }

    std::vector<std::string> GetRecordedDeltaHistogramNames()
    {
        return recorded_delta_histogram_names_;
    }

    int64_t GetRecordedDeltaHistogramSum(const std::string& name)
    {
        EXPECT_TRUE(ContainsKey(recorded_delta_histogram_sum_, name));
        return recorded_delta_histogram_sum_[name];
    }

private:
    std::vector<std::string> recorded_delta_histogram_names_;
    std::map<std::string, int64_t> recorded_delta_histogram_sum_;

    DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder);
};

class HistogramSnapshotManagerTest : public testing::Test {
protected:
    HistogramSnapshotManagerTest()
        : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting())
        , histogram_snapshot_manager_(&histogram_flattener_delta_recorder_)
    {
    }

    ~HistogramSnapshotManagerTest() override { }

    std::unique_ptr<StatisticsRecorder> statistics_recorder_;
    HistogramFlattenerDeltaRecorder histogram_flattener_delta_recorder_;
    HistogramSnapshotManager histogram_snapshot_manager_;
};

TEST_F(HistogramSnapshotManagerTest, PrepareDeltasNoFlagsFilter)
{
    // kNoFlags filter should record all histograms.
    UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4);
    UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);

    histogram_snapshot_manager_.PrepareDeltas(
        StatisticsRecorder::begin(false), StatisticsRecorder::end(),
        HistogramBase::kNoFlags, HistogramBase::kNoFlags);

    const std::vector<std::string>& histograms = histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
    EXPECT_EQ(2U, histograms.size());
    EXPECT_EQ("UmaHistogram", histograms[0]);
    EXPECT_EQ("UmaStabilityHistogram", histograms[1]);
}

TEST_F(HistogramSnapshotManagerTest, PrepareDeltasUmaHistogramFlagFilter)
{
    // Note that kUmaStabilityHistogramFlag includes kUmaTargetedHistogramFlag.
    UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4);
    UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);

    histogram_snapshot_manager_.PrepareDeltas(
        StatisticsRecorder::begin(false), StatisticsRecorder::end(),
        HistogramBase::kNoFlags, HistogramBase::kUmaTargetedHistogramFlag);

    const std::vector<std::string>& histograms = histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
    EXPECT_EQ(2U, histograms.size());
    EXPECT_EQ("UmaHistogram", histograms[0]);
    EXPECT_EQ("UmaStabilityHistogram", histograms[1]);
}

TEST_F(HistogramSnapshotManagerTest,
    PrepareDeltasUmaStabilityHistogramFlagFilter)
{
    UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4);
    UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);

    histogram_snapshot_manager_.PrepareDeltas(
        StatisticsRecorder::begin(false), StatisticsRecorder::end(),
        HistogramBase::kNoFlags, HistogramBase::kUmaStabilityHistogramFlag);

    const std::vector<std::string>& histograms = histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
    EXPECT_EQ(1U, histograms.size());
    EXPECT_EQ("UmaStabilityHistogram", histograms[0]);
}

TEST_F(HistogramSnapshotManagerTest, CheckMerge)
{
    UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4);
    UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);

    base::HistogramBase* h1 = base::LinearHistogram::FactoryGet(
        "UmaHistogram", 1, 4, 5, 0);
    ASSERT_TRUE(h1);
    base::HistogramBase* h2 = base::LinearHistogram::FactoryGet(
        "UmaStabilityHistogram", 1, 2, 3, 0);
    ASSERT_TRUE(h2);

    histogram_snapshot_manager_.StartDeltas();
    histogram_snapshot_manager_.PrepareDelta(h1);
    histogram_snapshot_manager_.PrepareDelta(h1); // Delta will be zero.
    histogram_snapshot_manager_.PrepareDelta(h2);
    h1->Add(2);
    h2->Add(1);
    histogram_snapshot_manager_.PrepareDelta(h2);
    histogram_snapshot_manager_.PrepareDelta(h1);
    histogram_snapshot_manager_.FinishDeltas();
    {
        const std::vector<std::string> histograms = histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
        EXPECT_EQ(2U, histograms.size());
        EXPECT_EQ(3, histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramSum("UmaHistogram"));
        EXPECT_EQ(2, histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramSum("UmaStabilityHistogram"));
    }
}

} // namespace base
